<?php


declare(strict_types=1);

namespace app\eslib;

/**
 * [弃用] Http 实用工具类
 * @version 1.0.0
 */
class HttpUtils
{
    /**
     * Ping IP 是否可用
     * 依赖：需要开启扩展 extension=sockets
     */
    public static function ping($ip, $port = 80)
    {
        if (strpos($ip, ':')) {
            list($ip, $port) = explode(':', $ip);
            $port = intval($port);
        }
        $socket = null;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
        } else if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        } else {
            return false;
        }
        return socket_connect($socket, $ip, $port);
    }

    /**
     * 发送一个POST请求
     * @param string $url     请求URL（带Http的完整地址）
     * @param array  $params  请求参数
     * @param array  $options 扩展参数
     * @return mixed|string
     */
    public static function post($url, $params = [], $options = [])
    {
        $req = self::sendRequest($url, $params, 'POST', $options);
        return $req;
    }

    /**
     * 发送一个GET请求
     * @param string $url     请求URL
     * @param array  $params  请求参数
     * @param array  $options 扩展参数
     * @return mixed|string
     */
    public static function get($url, $params = [], $options = [])
    {
        $req = self::sendRequest($url, $params, 'GET', $options);
        return $req;
    }

    /**
     * 返回标准格式的消息
     */
    static function stdMessage($success, $message, $data = null, $code = 0)
    {
        return ['success' => $success, 'message' => $message, 'data' => $data, 'code' => $code];
    }

    /**
     * CURL发送Request请求,含POST和REQUEST
     * @param string $url     请求的链接（带Http的完整地址）
     * @param mixed  $params  传递的参数
     * @param string $method  请求的方法
     * @param mixed  $options CURL的参数
     * @return array
     */
    public static function sendRequest($url, $params = [], $method = 'POST', $options = [])
    {
        if (!$url || 0 !== strpos($url, 'http')) {
            return self::stdMessage(false, 'URL地址错误: ' . $url);
        }
        $method = strtoupper($method);
        $protocol = substr($url, 0, 5);
        $query_string = is_array($params) ? http_build_query($params) : $params;

        $ch = curl_init();
        $defaults = [];
        if ('GET' == $method) {
            $geturl = $query_string ? ($url . (stripos($url, "?") !== false ? "&" : "?") . $query_string) : $url;
            $defaults[CURLOPT_URL] = $geturl; // 10002
        } else {
            $defaults[CURLOPT_URL] = $url; // 10002
            if ($method == 'POST') {
                $defaults[CURLOPT_POST] = 1;
            } else {
                $defaults[CURLOPT_CUSTOMREQUEST] = $method; // 10036
            }
            $defaults[CURLOPT_POSTFIELDS] = $query_string; // 10015
            // $defaults[CURLOPT_POSTFIELDS] = $params; // 10015
        }

        $defaults[CURLOPT_HEADER] = false; // 42
        $defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36"; // 10018
        $defaults[CURLOPT_FOLLOWLOCATION] = true; // 52
        $defaults[CURLOPT_RETURNTRANSFER] = true; // 19913
        $defaults[CURLOPT_CONNECTTIMEOUT] = 3; // 78
        $defaults[CURLOPT_TIMEOUT] = 3; // 13

        // disable 100-continue
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); // 10023

        if ('https' == $protocol) {
            $defaults[CURLOPT_SSL_VERIFYPEER] = false; // 64
            $defaults[CURLOPT_SSL_VERIFYHOST] = false; // 81
        }
        $options = (array)$options + $defaults;
        // foreach ($options as $option => $value) {
        //     echo 'try ',$option,'=',$value,'<br />';
        //     if (!curl_setopt($ch, $option, $value)) {
        //         echo 'error ',$option,'=',$value,'<br />';
        //         return false;
        //     } 
        // }
        curl_setopt_array($ch, $options);

        // curl请求失败或者超时不会触发异常，所以不用 try catch结构。
        // see: https://stackoverflow.com/questions/11297320/using-a-try-catch-with-curl-in-php
        $ret = curl_exec($ch); // mixed
        $err = curl_error($ch); // string

        if (false === $ret || !empty($err)) {
            $errno = curl_errno($ch); // string
            $info = curl_getinfo($ch); // array
            curl_close($ch);
            return self::stdMessage(false, $err, $info, $errno);
        }
        curl_close($ch);
        return self::stdMessage(true, '', $ret);
    }

    /**
     * 异步发送一个请求
     * @param string $url    请求的链接
     * @param mixed  $params 请求的参数
     * @param string $method 请求的方法
     * @return boolean TRUE
     */
    public static function sendAsyncRequest($url, $params = [], $method = 'POST')
    {
        $method = strtoupper($method);
        $method = $method == 'POST' ? 'POST' : 'GET';
        //构造传递的参数
        if (is_array($params)) {
            $post_params = [];
            foreach ($params as $k => &$v) {
                if (is_array($v)) {
                    $v = implode(',', $v);
                }
                $post_params[] = $k . '=' . urlencode($v);
            }
            $post_string = implode('&', $post_params);
        } else {
            $post_string = $params;
        }
        $parts = parse_url($url);
        //构造查询的参数
        if ($method == 'GET' && $post_string) {
            $parts['query'] = isset($parts['query']) ? $parts['query'] . '&' . $post_string : $post_string;
            $post_string = '';
        }
        $parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : '';
        //发送socket请求,获得连接句柄
        $fp = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80, $errno, $errstr, 3);
        if (!$fp) {
            return false;
        }
        //设置超时时间
        stream_set_timeout($fp, 3);
        $out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n";
        $out .= "Host: {$parts['host']}\r\n";
        $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $out .= "Content-Length: " . strlen($post_string) . "\r\n";
        $out .= "Connection: Close\r\n\r\n";
        if ($post_string !== '') {
            $out .= $post_string;
        }
        fwrite($fp, $out);
        //不用关心服务器返回结果
        //echo fread($fp, 1024);
        fclose($fp);
        return true;
    }

    /**
     * 发送文件到客户端
     * @param string $file
     * @param bool   $delaftersend
     * @param bool   $exitaftersend
     */
    public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true)
    {
        if (file_exists($file) && is_readable($file)) {
            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment;filename = ' . basename($file));
            header('Content-Transfer-Encoding: binary');
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0');
            header('Pragma: public');
            header('Content-Length: ' . filesize($file));
            ob_clean();
            flush();
            readfile($file);
            if ($delaftersend) {
                unlink($file);
            }
            if ($exitaftersend) {
                exit;
            }
        }
    }
}
